/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.community.service.paste;

import cn.ac.nercis.pes.common.constant.evaluation.EvaluationModule;
import cn.ac.nercis.pes.common.constant.knowledge.VirtualClassification;
import cn.ac.nercis.pes.common.constant.project.FinishState;
import cn.ac.nercis.pes.model.dto.common.DataCopy;
import cn.ac.nercis.pes.model.dto.common.DataPaste;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.evaluation.planning.EvaluationRequireDTO;
import cn.ac.nercis.pes.model.dto.knowledge.KmSimpleTargetDTO;
import cn.ac.nercis.pes.model.dto.project.SystemProcessDTO;
import cn.ac.nercis.pes.model.dto.target.EditApplyTargetDTO;
import cn.ac.nercis.pes.model.dto.target.EditSpecialTargetDTO;
import cn.ac.nercis.pes.model.dto.target.LayerTargetProcessDTO;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetRepository;
import cn.ac.nercis.pes.repository.model.evaluation.AssetDO;
import cn.ac.nercis.pes.service.evaluation.EvaluationRangeService;
import cn.ac.nercis.pes.service.evaluation.EvaluationRequireService;
import cn.ac.nercis.pes.service.project.SystemProcessService;
import cn.ac.nercis.pes.service.project.SystemTargetService;
import cn.ac.nercis.pes.service.utils.AssetUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;

/**
 * 方案编制阶段相关数据粘贴服务类
 *
 * @author limy
 * @version 1.0.0
 */
@Slf4j
@Service
public record PlanningPasteService(EvaluationRequireService requireService,
                                   SystemProcessService systemProcessService,
                                   EvaluationRequireService evaluationRequireService,
                                   SystemTargetService systemTargetService,
                                   EvaluationRangeService evaluationRangeService,
                                   AssetRepository assetRepository) {

    @Autowired
    public PlanningPasteService {
    }

    public Result<String> requireDataPaste(String currentSystemId, String currentTargetId, DataCopy dataCopy) {
        if (StringUtils.equals(currentSystemId, dataCopy.getCopySystemId())) {
            return Result.success();
        }
        var copyData = requireService.findEvaluationRequires(dataCopy.getCopySystemId(), dataCopy.getData());
        var currentData = requireService.findEvaluationRequires(currentSystemId, currentTargetId);
        List<EvaluationRequireDTO> modifyData = new ArrayList<>();
        for (var item : copyData) {
            var source = currentData.stream()
                    .filter(st -> StringUtils.equals(st.getProject(), item.getProject()))
                    .findFirst().orElse(null);
            if (Objects.isNull(source)) {
                item.setId("");
                item.setSystemId(currentSystemId);
            } else {
                item.setId(source.getId());
                item.setSystemId(source.getSystemId());
            }
            item.setLayerTargetId(currentTargetId);
            modifyData.add(item);
        }
        var opResult = requireService.batchSaveEvaluationRequires(modifyData);
        if (opResult.isSuccess()) {
            evalRequireProcessPaste(currentSystemId, currentTargetId, dataCopy);
            return opResult;
        } else {
            return Result.failed("粘贴失败！");
        }
    }

    public Result<String> contentConfirmDataPaste(DataPaste dataPaste, DataCopy dataCopy) {
        if (dataCopy.getName().equals(dataPaste.getName())) {
            if ((StringUtils.isBlank(dataCopy.getData()) && StringUtils.isBlank(dataPaste.getData()) && dataCopy.getCopySystemId().equals(dataPaste.getSystemId())) || (StringUtils.isNotBlank(dataCopy.getData()) && StringUtils.isNotBlank(dataPaste.getData()) && dataCopy.getData().equals(dataPaste.getData()))) {
                return Result.success();
            }
        }
        var virtualClass = Stream.of(VirtualClassification.values())
                .filter(vc -> StringUtils.endsWith(dataPaste.getName(), vc.getName())).findFirst()
                .orElse(null);
        if (Objects.isNull(virtualClass)) {
            return Result.failed("粘贴失败，未找到对应的虚拟系统资产分类");
        }
        if (StringUtils.isNotBlank(dataPaste.getData())) {
            return contentConfirmDataPasteByAsset(virtualClass, dataPaste.getData(), dataCopy.getData());
        } else {
            return contentConfirmDataPasteByVirtualClass(virtualClass, dataPaste.getSystemId(), dataCopy.getCopySystemId());
        }
    }

    private Result<String> contentConfirmDataPasteByAsset(VirtualClassification vc, String pasteAssetId, String copyAssetId) {
        //获取粘贴资产对象信息
        var pasteAsset = assetRepository.findById(pasteAssetId).orElse(null);
        if(Objects.isNull(pasteAsset)){
            return Result.failed("未找到粘贴的资产对象信息");
        }
        //复制的资产对象信息
        var copyAsset = assetRepository.findById(copyAssetId).orElse(null);
        if(Objects.isNull(copyAsset)){
            return Result.failed("未找到复制的资产对象信息");
        }
        return contentConfirmDataPasteByAsset(pasteAsset,List.of(copyAsset),true);
    }

    private Result<String> contentConfirmDataPasteByAsset(AssetDO pasteAsset, List<AssetDO> copyAssets, boolean defaultSelected){
        AssetDO copyAsset;
        if(defaultSelected){
            copyAsset = copyAssets.stream().findFirst().orElse(null);
        }else{
            copyAsset = copyAssets.stream()
                    .filter(a->StringUtils.equals(AssetUtils.getAssetName(a),AssetUtils.getAssetName(pasteAsset)))
                    .findFirst()
                    .orElse(null);
        }
        if(Objects.isNull(copyAsset)){
            return Result.failed("未找到复制的资产对象信息");
        }
        //获取粘贴资产对象关联层面指标ID
        var pasteLayerTargetId = getLayerTargetIdByAsset(pasteAsset);
        if(StringUtils.isEmpty(pasteLayerTargetId)){
            return Result.failed("未找到粘贴的资产对象所属层面指标");
        }
        var copyLayerTargetId = getLayerTargetIdByAsset(copyAsset);
        if(StringUtils.isEmpty(copyLayerTargetId)){
            return Result.failed("未找到复制的资产对象所属层面指标");
        }

        layerEvalMethodPaste(copyAsset,copyLayerTargetId,pasteAsset,pasteLayerTargetId);
        var copyUnitMethods = evaluationRangeService.findUnitEvalMethods(copyAsset.getSystemId(),copyAsset.getId(),copyLayerTargetId);
        if(CollectionUtils.isEmpty(copyUnitMethods)){
            return Result.failed("未找到复制的资产对象对应测评方式及方法信息");
        }
        var pasteUnitMethods = evaluationRangeService.findUnitEvalMethods(pasteAsset.getSystemId(),pasteAsset.getId(),pasteLayerTargetId)
                .stream()
                .map(uem->{
                    var copy = copyUnitMethods.stream()
                            .filter(cum -> StringUtils.equals(cum.getUnitTargetName(),uem.getUnitTargetName()))
                            .findFirst()
                            .orElse(null);
                    if(Objects.isNull(copy)){
                        return null;
                    }
                    uem.setMethod(copy.getMethod());
                    uem.setDesc(copy.getDesc());
                    return uem;
                })
                .filter(Objects::nonNull)
                .toList();
        if(CollectionUtils.isNotEmpty(pasteUnitMethods)){
            this.evaluationRangeService.saveUnitEvalMethods(pasteUnitMethods);
            return Result.success();
        }else{
            return Result.failed("未找到复制的资产对象对应测评方式及方法信息");
        }
    }

    private Result<String> contentConfirmDataPasteByVirtualClass(VirtualClassification vc, String pasteSystemId, String copySystemId){
        //获取粘贴资产对象信息列表
        var pasteAssets = assetRepository.findAllBySystemIdAndClassIdIn(pasteSystemId,Set.copyOf(vc.getClassIds()))
                .stream()
                .filter(a->Objects.nonNull(a.getIsUse()) && a.getIsUse())
                .toList();
        var copyAssets = assetRepository.findAllBySystemIdAndClassIdIn(copySystemId,Set.copyOf(vc.getClassIds()))
                .stream()
                .filter(a->Objects.nonNull(a.getIsUse()) && a.getIsUse())
                .toList();
        //复制的虚拟资产分类下的资产对象信息列表
        if(vc != VirtualClassification.SECURITY_MANAGE){
            var pa = pasteAssets.stream().findFirst().orElse(null);
            var ca = copyAssets.stream().findFirst().orElse(null);
            if(Objects.isNull(pa)){
                return Result.failed("未找相匹配粘贴的系统资产");
            }
            if(Objects.isNull(ca)){
                return Result.failed("未找相匹配复制的系统资产");
            }
            var pasteLayerTargetId = getLayerTargetIdByAsset(pa);
            var copyLayerTargetId = getLayerTargetIdByAsset(ca);
            if(StringUtils.isEmpty(copyLayerTargetId)){
                return Result.failed("未找到复制的资产对象所属层面指标");
            }
            if(StringUtils.isEmpty(pasteLayerTargetId)){
                return Result.failed("未找到粘贴的资产对象所属层面指标");
            }
            copyAssets.forEach(copyAsset->contentConfirmDataPaste(pasteAssets,pasteLayerTargetId,copyAsset,copyLayerTargetId));
        }else{
            pasteAssets.forEach(pa->contentConfirmDataPasteByAsset(pa,copyAssets,false));
        }
        return Result.success();
    }

    private void contentConfirmDataPaste(List<AssetDO> pasteAssets,String pasteLayerTargetId,AssetDO copyAsset,String copyLayerTargetId){
        var pasteAsset = pasteAssets
                .stream()
                .filter(pa->StringUtils.equals(AssetUtils.getAssetName(pa),AssetUtils.getAssetName(copyAsset)))
                .findFirst()
                .orElse(null);
        if(Objects.isNull(pasteAsset)){
            return;
        }
        layerEvalMethodPaste(copyAsset,copyLayerTargetId,pasteAsset,pasteLayerTargetId);
        var copyUnitMethods = evaluationRangeService.findUnitEvalMethods(copyAsset.getSystemId(),copyAsset.getId(),copyLayerTargetId);
        var pasteUnitMethods = evaluationRangeService.findUnitEvalMethods(pasteAsset.getSystemId(),pasteAsset.getId(),pasteLayerTargetId)
                .stream()
                .map(uem->{
                    var copy = copyUnitMethods.stream()
                            .filter(cum -> StringUtils.equals(cum.getUnitTargetName(),uem.getUnitTargetName()))
                            .findFirst()
                            .orElse(null);
                    if(Objects.isNull(copy)){
                        return null;
                    }
                    uem.setMethod(copy.getMethod());
                    uem.setDesc(copy.getDesc());
                    return uem;
                })
                .filter(Objects::nonNull)
                .toList();
        if(CollectionUtils.isNotEmpty(pasteUnitMethods)){
            this.evaluationRangeService.saveUnitEvalMethods(pasteUnitMethods);
        }
    }

    private void layerEvalMethodPaste(AssetDO copyAsset,String copyLayerTargetId,AssetDO pasteAsset,String pasteLayerTargetId){
        var copyLayerMethod = evaluationRangeService.getLayerEvalMethod(copyAsset.getSystemId(),copyAsset.getId(),copyLayerTargetId).getData();
        var pasteLayerMethod = evaluationRangeService.getLayerEvalMethod(pasteAsset.getSystemId(),pasteAsset.getId(),pasteLayerTargetId).getData();
        if(Objects.nonNull(pasteLayerMethod) && Objects.nonNull(copyLayerMethod)){
            pasteLayerMethod.setMethod(copyLayerMethod.getMethod());
            pasteLayerMethod.setDesc(copyLayerMethod.getDesc());
            this.evaluationRangeService.saveLayerEvalMethod(pasteLayerMethod,copyLayerMethod.getFinishState());
        }
    }

    private String getLayerTargetIdByAsset(AssetDO assetDO){
        var result = evaluationRangeService.findLayerTargetByAsset(assetDO.getSystemId(),assetDO.getId());
        if(result.isSuccess()){
            return result.getData().stream().findFirst().map(KmSimpleTargetDTO::getId).orElse(StringUtils.EMPTY);
        }else{
            return StringUtils.EMPTY;
        }
    }

    public Result<String> targetDataPaste(String currentSystemId, String currentTargetId, DataCopy dataCopy) {
        if (StringUtils.equals(currentSystemId, dataCopy.getCopySystemId())) {
            return Result.success();
        }
        Result<String> result = systemTargetPaste(currentSystemId, currentTargetId, dataCopy);
        Result<String> re = systemSpecialTargetPaste(currentSystemId, currentTargetId, dataCopy);
        if (result.isSuccess() && re.isSuccess()) {
            evalTargetProcessPaste(currentSystemId, dataCopy);
            return Result.success();
        }
        return Result.failed("粘贴失败！");
    }

    private Result<String> systemSpecialTargetPaste(String currentSystemId, String currentTargetId, DataCopy dataCopy) {
        var copySpecialData = systemTargetService.findUnitSpecialTargets(dataCopy.getCopySystemId(), dataCopy.getData());
        var currentSpecialData = systemTargetService.findUnitSpecialTargets(currentSystemId, currentTargetId);
        List<EditSpecialTargetDTO> editSpecialTargetDTOList = new ArrayList<>();
        for (var item : copySpecialData) {
            var source = currentSpecialData.stream()
                    .filter(st -> StringUtils.equals(st.getName(), item.getName()))
                    .findFirst().orElse(null);
            EditSpecialTargetDTO dto = new EditSpecialTargetDTO();
            if (Objects.isNull(source)) {
                dto.setId("");
            } else {
                dto.setId(source.getId());
            }
            dto.setSystemId(currentSystemId);
            dto.setLayerTargetId(currentTargetId);
            dto.setName(item.getName());
            dto.setSpecialType(item.getSpecialType());
            dto.setTargetDesc(item.getTargetDesc());
            dto.setReasons(item.getReasons());
            dto.setApplyClaim(item.getKmApplyClaim());
            dto.setKmTargetId(item.getKmTargetId());
            editSpecialTargetDTOList.add(dto);
        }
        var re = systemTargetService.batchSaveSpecialTarget(editSpecialTargetDTOList);
        return re;
    }

    private Result<String> systemTargetPaste(String currentSystemId, String currentTargetId, DataCopy dataCopy) {
        var copyData = this.systemTargetService.findUnitTarget(dataCopy.getCopySystemId(), dataCopy.getData());
        var currentData = systemTargetService.findUnitTarget(currentSystemId, currentTargetId);
        List<EditApplyTargetDTO> editApplyTargetDTOList = new ArrayList<>();
        for (var item : copyData) {
            var source = currentData.stream()
                    .filter(st -> StringUtils.equals(st.getName(), item.getName()))
                    .findFirst().orElse(null);
            if (Objects.nonNull(source)) {
                var editApplyTargetDTO = EditApplyTargetDTO.builder()
                        .id(source.getId())
                        .systemId(source.getSystemId())
                        .isApply(item.getIsApply())
                        .reasons(item.getReasons())
                        .build();
                editApplyTargetDTOList.add(editApplyTargetDTO);
            }
        }
        var result = systemTargetService.batchModifyApplyTarget(editApplyTargetDTOList);
        return result;
    }

    private void evalTargetProcessPaste(String currentSystemId, DataCopy dataCopy) {
        EvaluationModule currentModule = EvaluationModule.fromNameByTargetConfirm(dataCopy.getName().split("/")[1]);
        var systemProcess = systemProcessService.findTargetConfirmProcess(dataCopy.getCopySystemId());
        var finishState = systemProcess.stream()
                .filter(sp -> StringUtils.contains(sp.getName(), dataCopy.getName().split("/")[1]))
                .map(SystemProcessDTO::getFinishState)
                .findFirst().orElse(FinishState.NONE);
        systemProcessService.setSystemProcess(currentSystemId, currentModule, finishState);
    }

    private void evalRequireProcessPaste(String systemId, String currentTargetId, DataCopy dataCopy) {
        var copyProcess = evaluationRequireService.findEvaluationProcess(dataCopy.getCopySystemId());
        if (Objects.isNull(copyProcess)) {
            return;
        }
        var finishState = copyProcess.stream()
                .filter(sp -> StringUtils.contains(sp.getName(), dataCopy.getName().split("/")[1]))
                .map(LayerTargetProcessDTO::getState)
                .findFirst().orElse(FinishState.NONE);
        evaluationRequireService.setEvaluationRequireFinishState(systemId, currentTargetId, dataCopy.getName(), finishState);
    }

}